package com.cloudfun.campusshare.service;

import com.alibaba.fastjson.JSONObject;
import com.cloudfun.campusshare.common.constant.*;
import com.cloudfun.campusshare.common.model.vo.*;
import com.cloudfun.campusshare.common.response.BaseResult;
import com.cloudfun.campusshare.common.response.BaseResultFactory;
import com.cloudfun.campusshare.entity.DeviceAlarmEntity;
import com.cloudfun.campusshare.entity.DeviceEntity;
import com.cloudfun.campusshare.entity.DevicePropertyEntity;
import com.cloudfun.campusshare.repo.DeviceAlarmRepo;
import com.cloudfun.campusshare.repo.DeviceEntityRepo;
import com.cloudfun.campusshare.repo.DevicePropertyRepo;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * Created by Huqin on 2020/3/23
 */
@Transactional
public abstract class AbstractDeviceOpService {

    @Autowired
    protected DeviceService deviceService;

    @Autowired
    protected DeviceEntityRepo deviceEntityRepo;

    @Autowired
    protected DevicePropertyService devicePropertyService;

    @Autowired
    protected DeviceAlarmRepo deviceAlarmRepo;

    @Autowired
    protected DevicePropertyRepo devicePropertyRepo;

    @Autowired
    private SysDictService sysDictService;

    // 滚筒洗衣机8公斤
    protected static Map<String, Integer> washTypeRemainTimeMapGXY8 = new HashMap<>();
    // 滚筒洗衣机13.5公斤
    protected static Map<String, Integer> washTypeRemainTimeMapGXY13_5 = new HashMap<>();

    // 波轮洗衣机-无消毒
    protected static Map<Integer, Integer> washTypeRemainTimeMapBXY6_5 = new HashMap<>();
    // 波轮洗衣机-有消毒
    protected static Map<Integer, Integer> washTypeRemainTimeMapBXYD6_5 = new HashMap<>();
    // 波轮洗鞋机
    protected static Map<Integer, Integer> washTypeRemainTimeMapBXX6_5 = new HashMap<>();

    static {
        washTypeRemainTimeMapGXY8.put("1", 40);
        washTypeRemainTimeMapGXY8.put("2", 25);
        washTypeRemainTimeMapGXY8.put("3", 50);
        washTypeRemainTimeMapGXY8.put("4", 6);
        washTypeRemainTimeMapGXY8.put("5", 6);
        washTypeRemainTimeMapGXY8.put("106", 47);
        washTypeRemainTimeMapGXY8.put("107", 31);
        washTypeRemainTimeMapGXY8.put("108", 57);
        washTypeRemainTimeMapGXY8.put("116", 40);
        washTypeRemainTimeMapGXY8.put("118", 50);
        washTypeRemainTimeMapGXY8.put("119", 47);
        washTypeRemainTimeMapGXY8.put("121", 57);
        washTypeRemainTimeMapGXY8.put("122", 40);
        washTypeRemainTimeMapGXY8.put("124", 50);
        washTypeRemainTimeMapGXY8.put("125", 47);
        washTypeRemainTimeMapGXY8.put("127", 57);

        washTypeRemainTimeMapGXY13_5.put("1", 32);
        washTypeRemainTimeMapGXY13_5.put("2", 32);
        washTypeRemainTimeMapGXY13_5.put("3", 32);
        washTypeRemainTimeMapGXY13_5.put("4", 7);
        washTypeRemainTimeMapGXY13_5.put("5", 6);
        washTypeRemainTimeMapGXY13_5.put("8", 32);
        washTypeRemainTimeMapGXY13_5.put("113", 42);
        washTypeRemainTimeMapGXY13_5.put("114", 42);
        washTypeRemainTimeMapGXY13_5.put("115", 42);

        washTypeRemainTimeMapBXYD6_5.put(1, 35);
        washTypeRemainTimeMapBXYD6_5.put(2, 15);
        washTypeRemainTimeMapBXYD6_5.put(3, 56);
        washTypeRemainTimeMapBXYD6_5.put(4, 6);
        washTypeRemainTimeMapBXYD6_5.put(5, 2);

        washTypeRemainTimeMapBXY6_5.put(1, 35);
        washTypeRemainTimeMapBXY6_5.put(2, 25);
        washTypeRemainTimeMapBXY6_5.put(3, 45);
        washTypeRemainTimeMapBXY6_5.put(4, 6);
        washTypeRemainTimeMapBXY6_5.put(5, 2);

        washTypeRemainTimeMapBXX6_5.put(1, 31);
        washTypeRemainTimeMapBXX6_5.put(2, 29);
        washTypeRemainTimeMapBXX6_5.put(3, 35);
        washTypeRemainTimeMapBXX6_5.put(4, 6);
        washTypeRemainTimeMapBXX6_5.put(5, 2);
    }

    public abstract DevicePlatform supportPlatform();

    public BaseResult<String> getPropertyByName(DeviceEntity deviceEntity, String propertyName) {
        return this.doGetPropertyByName(deviceEntity, propertyName);
    }

    protected abstract BaseResult<String> doGetPropertyByName(DeviceEntity deviceEntity, String propertyName);

    public BaseResult getCautionList(DeviceEntity imei) {
        return this.doGetCautionList(imei);
    }

    protected BaseResult doGetCautionList(DeviceEntity deviceEntity) {
        return BaseResultFactory.produceResult(Code.SUCCESS, this.getLocalCautionList(deviceEntity.getId()));
    }

    public BaseResult deviceOnline(DeviceEntity deviceEntity) {
        String localDeviceOnline = getLocalDeviceOnline(deviceEntity);
        if (StringUtils.isNotBlank(localDeviceOnline)) {
            return BaseResultFactory.produceResult(Code.SUCCESS, localDeviceOnline);
        }
        return this.doDeviceOnline(deviceEntity);
    }

    protected abstract BaseResult doDeviceOnline(DeviceEntity deviceEntity);

    public BaseResult reset(DeviceEntity deviceEntity) {
        if (DeviceStatus.NOT_RUNNING.equals(deviceEntity.getStatus())) {
            return BaseResultFactory.produceEmptyResult(Code.ERROR, "设备未启动");
        }
        BaseResult res = doReset(deviceEntity);
        if (res.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(DeviceStatus.STOPPING);
            deviceEntityRepo.save(deviceEntity);
        }
        return res;
    }

    public abstract BaseResult doReset(DeviceEntity deviceEntity);

    public BaseResult wash(DeviceEntity deviceEntity, RollerWasherOpVO rollerWasherOpVO) {
        BaseResult<?> baseResult = this.checkDevice(deviceEntity, true, true);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }
        String washType = rollerWasherOpVO.getWashType();
        Map<String, Integer> washTypeTimeMap = DeviceType.GXY8.getName().equals(rollerWasherOpVO.getDeviceType()) ?
                washTypeRemainTimeMapGXY8 : washTypeRemainTimeMapGXY13_5;
        if (!washTypeTimeMap.containsKey(washType)) {
            return BaseResultFactory.produceEmptyResult(Code.PARAMETER_ERROR, "洗涤类型错误");
        }

        baseResult = this.doWash(deviceEntity, rollerWasherOpVO);

        if (baseResult.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(DeviceStatus.STARTING);
            deviceEntityRepo.save(deviceEntity);
            // 设置剩余时间
            Integer remainTime = washTypeTimeMap.get(washType);
            devicePropertyService.setProperty(deviceEntity.getId(), "remainingTimeMM", remainTime.toString());
        }
        return baseResult;
    }

    protected abstract BaseResult doWash(DeviceEntity deviceEntity, RollerWasherOpVO rollerWasherOpVO);

    public BaseResult dry(DeviceEntity deviceEntity, DryerOperateVO deviceDryingVO) {
        //针对6公斤干衣机判断平台
        if (BaseConstant.GGY6_NAME.equals(deviceEntity.getDeviceType()) && DevicePlatform.UHOME.equals(deviceEntity.getPlatform())) {
            return BaseResultFactory.produceResult(Code.PLATFORM_ERROR, "设备所属平台错误");
        }
        BaseResult<?> baseResult = this.checkDevice(deviceEntity, true, true);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }

        deviceDryingVO.setStartEnabled(true);
        baseResult = this.doDrying(deviceEntity, deviceDryingVO);

        if (baseResult.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(DeviceStatus.STARTING);
            deviceEntityRepo.save(deviceEntity);
            // 设置剩余时间
            devicePropertyService.setProperty(deviceEntity.getId(), "remainingTimeMM", deviceDryingVO.getDryTime().toString());
        }
        return baseResult;
    }

    public abstract BaseResult doDrying(DeviceEntity deviceEntity, DryerOperateVO deviceDryingVO);

    public BaseResult pulseWash(DeviceEntity deviceEntity, PulseWasherOperateVO operateVO) {
        BaseResult baseResult = this.checkDevice(deviceEntity, true, true);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }
        Integer washType = operateVO.getWashType();
        Map<Integer, Integer> washTypeTimeMap = DeviceType.BXY6_5.getName().equals(operateVO.getDeviceType()) ?
                washTypeRemainTimeMapBXY6_5 : washTypeRemainTimeMapBXYD6_5;
        if (!washTypeTimeMap.containsKey(washType)) {
            return BaseResultFactory.produceEmptyResult(Code.PARAMETER_ERROR, "洗涤类型错误");
        }

        baseResult = this.doPulseWash(deviceEntity, operateVO);

        if (baseResult.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(DeviceStatus.STARTING);
            deviceEntityRepo.save(deviceEntity);
            // 设置剩余时间
            Integer remainTime = washTypeTimeMap.get(washType);
            devicePropertyService.setProperty(deviceEntity.getId(), "remainingTimeMM", remainTime.toString());
        }
        return baseResult;
    }

    public abstract BaseResult doPulseWash(DeviceEntity deviceEntity, PulseWasherOperateVO operateVO);

    public BaseResult shoeWash(DeviceEntity deviceEntity, ShoeWasherOperateVO operateVO) {
        BaseResult baseResult = this.checkDevice(deviceEntity, true, true);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }

        baseResult = this.doShoeWash(deviceEntity, operateVO);

        if (baseResult.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(DeviceStatus.STARTING);
            deviceEntityRepo.save(deviceEntity);
            // 设置剩余时间
            Integer remainTime = washTypeRemainTimeMapBXX6_5.get(operateVO.getWashType());
            devicePropertyService.setProperty(deviceEntity.getId(), "remainingTimeMM", remainTime.toString());
        }
        return baseResult;
    }

    public abstract BaseResult doShoeWash(DeviceEntity deviceEntity, ShoeWasherOperateVO operateVO);

    public BaseResult<?> deviceVerifyCode(DeviceEntity deviceEntity, String verifyCode) {
        BaseResult<?> baseResult = this.checkDevice(deviceEntity, true, true);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }
        return this.doSendDeviceVerifyCode(deviceEntity, verifyCode);
    }

    public abstract BaseResult doSendDeviceVerifyCode(DeviceEntity deviceEntity, String verifyCode);

    protected BaseResult<String> checkDevice(DeviceEntity deviceEntity, boolean needOnline, boolean needIdle) {
        if (needOnline) {
            BaseResult<?> res = this.deviceOnline(deviceEntity);
            boolean online;
            if (res.getCode() == Code.SUCCESS.getCode()) {
                online = JSONObject.parseObject(res.getData().toString()).getBoolean("isOnline");
            } else {
                online = false;
            }
            // 设备应该在线
            if (!online) {
                return BaseResultFactory.produceResult(Code.DEVICE_OFFLINE, "设备离线");
            }
        }
        if (needIdle) {
            // 设备应该不在工作状态
            if (DeviceStatus.RUNNING.equals(deviceEntity.getStatus()) || DeviceStatus.STOPPING.equals(deviceEntity.getStatus())) {
                return BaseResultFactory.produceResult(Code.DEVICE_RUNNING, "设备正在运行");
            }
        }
        return BaseResultFactory.produceResult(Code.SUCCESS, "成功");
    }

    /**
     * 获取本地设备属性
     *
     * @param deviceId
     * @param propName
     * @return
     */
    protected String getLocalAttrByName(String deviceId, String propName) {
        DevicePropertyEntity devicePropertyEntity = devicePropertyRepo.findByDeviceIdAndPropertyName(deviceId, propName);
        if (null == devicePropertyEntity) {
            return null;
        }
        return this.mockUhomePropertyQueryResult(devicePropertyEntity);
    }

    /**
     * 获取本地设备告警
     *
     * @param deviceId
     * @return
     */
    protected String getLocalCautionList(String deviceId) {
        List<DeviceAlarmEntity> deviceAlarmEntities = deviceAlarmRepo.findAllByDeviceIdAndValid(deviceId, true);
        return this.mockUhomeAlarmQueryResult(deviceAlarmEntities);
    }

    /**
     * 本地获取设备是否上线
     *
     * @param deviceEntity
     * @return
     */
    protected String getLocalDeviceOnline(DeviceEntity deviceEntity) {
        if (null == deviceEntity || null == deviceEntity.getOnline()) {
            return null;
        }
        return this.mockUhomeOnlineResult(deviceEntity.getOnline());
    }

    /**
     * 模拟U+的查询属性返回值
     *
     * @param devicePropertyEntity
     * @return
     */
    protected String mockUhomePropertyQueryResult(DevicePropertyEntity devicePropertyEntity) {
        return "{\"data\":{\"attributes\":[{\"name\":\"#{propName}\",\"value\":\"#{propVal}\"}]},\"retCode\":\"000000\",\"retInfo\":\"成功\"}"
                .replace("#{propName}", devicePropertyEntity.getPropertyName())
                .replace("#{propVal}", devicePropertyEntity.getPropertyValue());
    }

    protected String mockSuccess() {
        return "{\"retCode\":\"00000\",\"retInfo\":\"成功!\"}";
    }

    protected String mockFail(String msg) {
        return "{\"retCode\":\"000001\",\"retInfo\":\"" + msg + "\"}";
    }

    /**
     * 模拟U+的查询告警返回值
     *
     * @param deviceAlarmEntities
     * @return
     */
    protected String mockUhomeAlarmQueryResult(List<DeviceAlarmEntity> deviceAlarmEntities) {
        StringBuilder stringBuilder = new StringBuilder("{\"cautionListInfo\":[");
        List<String> alarmInfoList = new ArrayList<>();
        for (DeviceAlarmEntity alarmEntity : deviceAlarmEntities) {
            String alarmInfo = "{\"name\":\"" + alarmEntity.getAlarmName() +
                    "\",\"timestamp\":" +
                    Long.parseLong(alarmEntity.getCreateTime()) / 1000 +
                    ",\"value\":\"" +
                    alarmEntity.getAlarmName() +
                    "\"}";
            alarmInfoList.add(alarmInfo);
        }
        stringBuilder.append(StringUtils.join(alarmInfoList, ","));
        stringBuilder.append("],\"retCode\":\"00000\",\"retInfo\":\"成功\"}");
        return stringBuilder.toString();
    }

    /**
     * 模拟U+的查询是否上线返回值
     *
     * @param online
     * @return
     */
    protected String mockUhomeOnlineResult(boolean online) {
        return "{\"isOnline\":" + online + ",\"retCode\":\"00000\",\"retInfo\":\"成功!\"}";
    }

    public BaseResult<?> getProperties(DeviceEntity deviceEntity) {
        BaseResult<Map<String, String>> baseResult = this.doGetProperties(deviceEntity);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }
        // 只返回对外部可见的属性
        Map<String, String> propMap = baseResult.getData();
        Map<String, String> availablePropMap = sysDictService.getDictByType(SysDictType.AVAILABLE_DEVICE_PROPERTY);
        Map<String, String> resMap = new HashMap<>();
        for (String availablePropertyName : availablePropMap.keySet()) {
            if (propMap.containsKey(availablePropertyName)) {
                resMap.put(availablePropertyName, propMap.get(availablePropertyName));
            }
        }
        baseResult.setData(resMap);
        return baseResult;
    }

    protected abstract BaseResult<Map<String, String>> doGetProperties(DeviceEntity deviceEntity);

    /**
     * 启动液体泵
     * @param deviceEntity 设备信息
     * @param pumpWorkVO 设置工作时间
     * @return
     */
    public BaseResult<?> pumpStart(DeviceEntity deviceEntity, PumpWorkVO pumpWorkVO){
        BaseResult baseResult = checkDevice(deviceEntity);
        if (baseResult.getCode() != Code.SUCCESS.getCode()) {
            return baseResult;
        }
        baseResult = this.doPumpStart(deviceEntity, pumpWorkVO);
        return updateDeviceStatus(baseResult,deviceEntity,DeviceStatus.STARTING);
    }

    protected BaseResult checkDevice(DeviceEntity deviceEntity){
        if (DeviceStatus.RUNNING.equals(deviceEntity.getStatus())
                || DeviceStatus.STARTING.equals(deviceEntity.getStatus())) {
            return BaseResultFactory.produceResult(Code.DEVICE_RUNNING, "设备正在尝试运行");
        }
        if(DeviceStatus.ABNORMAL.equals(deviceEntity.getStatus())){
            return BaseResultFactory.produceResult(Code.DEVICE_ABNORMAL, "设备异常");
        }
        return BaseResultFactory.produceSuccess();
    }

    protected BaseResult updateDeviceStatus(BaseResult baseResult,DeviceEntity deviceEntity,DeviceStatus status){
        if (baseResult.getCode() == Code.SUCCESS.getCode()) {
            // 更新运行状态
            deviceEntity.setStatus(status);
            deviceEntityRepo.save(deviceEntity);
        }
        return baseResult;
    }

    protected abstract BaseResult doPumpStart(DeviceEntity deviceEntity, PumpWorkVO pumpWorkVO);
}
